#include <iostream>
#include <sstream>
#include <cmath>
using namespace std;

#include "MyString.h"

#define STR_EQUAL(actual, expected) \
    str_equal((actual), (expected), __FUNCTION__, __LINE__)

#define INT_EQUAL(actual, expected) \
    int_equal((actual), (expected), __FUNCTION__, __LINE__)

#define FLOAT_EQUAL(actual, expected)   \
    float_equal((actual), (expected), __FUNCTION__, __LINE__)

namespace
{
void str_equal(string actual, string expected, const char *func, int line)
{
    if (actual != expected)
    {
        cout << func << "():" << line << endl
             <<"expected \"" << expected 
             << "\", actual \"" << actual
             << "\"" << endl;
    }
}

void int_equal(int actual, int expected, const char *func, int line)
{
    if (actual != expected)
    {
        cout << func << "():" << line << endl
             << "expected " << expected
             << ", actual " << actual
             << endl;
    }
}

void float_equal(float actual, float expected, const char *func, int line)
{
    if (fabs(actual - expected) > 1e-8)
    {
        cout << func << "():" << line << endl
             << "expected " << expected
             << ", actual " << actual
             << endl;
    }
}

}

static void
default_contructor(void)
{
    MyString s;
    ostringstream ss;
    ss << s;
    STR_EQUAL(ss.str(), "");
}

static void
int_contructor(void)
{
    MyString s(567);
    ostringstream ss;
    ss << s;
    STR_EQUAL(ss.str(), "567");
}

static void
float_contructor(void)
{
    MyString s(123.456f);
    ostringstream ss;
    ss << s;
    STR_EQUAL(ss.str(), "123.5");
}

static void
cstr_contructor(void)
{
    MyString s("hello");
    ostringstream ss;
    ss << s;
    STR_EQUAL(ss.str(), "hello");
}

static void
copy_contructor(void)
{
    MyString temp("world");
    MyString s(temp);
    ostringstream ss;
    ss << s;
    STR_EQUAL(ss.str(), "world");
}

static void
operator_plus(void)
{
    MyString s1("hello ");
    MyString s2("world!");

    {
        ostringstream ss;
        ss << s1 + s2;
        STR_EQUAL(ss.str(), "hello world!");
    }
}

static void
operator_minus(void)
{
    MyString s1("testing 123");

    {
        ostringstream ss;
        ss << s1 - "ing";
        STR_EQUAL(ss.str(), "test 123");
    }

    {
        ostringstream ss;
        ss << s1 - "abc";
        STR_EQUAL(ss.str(), "testing 123");
    }
}

static void
test_subString(void)
{
    MyString s("abcdefg");

    {
        ostringstream ss;
        ss << s.subString(4, 3);
        STR_EQUAL(ss.str(), "efg");
    }

    {
        ostringstream ss;
        ss << s.subString(4, 5);
        STR_EQUAL(ss.str(), "efg");
    }

    {
        ostringstream ss;
        ss << s.subString(7, 1);
        STR_EQUAL(ss.str(), "");
    }
}

static void
test_find(void)
{
    MyString s("testing 123");
    INT_EQUAL(s.find("ing"), 4);
    INT_EQUAL(s.find("abc"), -1);
}

static void
test_toInt(void)
{
    {
        int x = 123;
        MyString s(x);
        INT_EQUAL(s.toInt(), x);

        try {
            s = "abc";
            INT_EQUAL(s.toInt(), -2);
        }
        catch (convert_error& ce) {
            cout << ce;
        }
    }

    {
        int x = 0x7fffffff;
        MyString s(x);
        INT_EQUAL(s.toInt(), x);
    }

    {
        int x = 1 << 31;
        MyString s(x);
        INT_EQUAL(s.toInt(), x);
    }

    {
        MyString s("9123456789");
        INT_EQUAL(s.toInt(), 912345678);
    }

    {
        MyString s("-----123");
        INT_EQUAL(s.toInt(), -123);
    }

    {
        MyString s("-+-+-123");
        INT_EQUAL(s.toInt(), -123);
    }
}

static void
test_toFloat(void)
{
    float x = 123.5f;
    MyString s(x);
    FLOAT_EQUAL(s.toFloat(), x);

    try {
        s = "abc";
        FLOAT_EQUAL(s.toFloat(), -2.0f);
    }
    catch (convert_error& ce) {
        cout << ce;
    }
}

int main(void)
{
    default_contructor();
    int_contructor();
    float_contructor();
    cstr_contructor();
    copy_contructor();

    operator_plus();
    operator_minus();

    test_subString();
    test_find();

    test_toInt();
    test_toFloat();

    return 0;
}

